package com.xuzhiguang.baidu.pan.sdk.common.http;

import org.apache.commons.lang3.ArrayUtils;

import javax.activation.MimetypesFileTypeMap;
import java.util.*;

/**
 * @author xuzhiguang
 * @date 2020/6/27 3:36
 */
public class MultipartBodyRequestBuilder {

    private final String boundary;

    private final List<Part> parts = new ArrayList<>();

    private final Request request;

    private final MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();

    public MultipartBodyRequestBuilder() {
        request = new Request();
        this.boundary = UUID.randomUUID().toString().replaceAll("-", "");
        request.getHeaders().put("Content-Type", "multipart/form-data; boundary=" + this.boundary);
        request.setHttpMethod(HttpMethod.POST);
    }

    public MultipartBodyRequestBuilder url(String url) {
        request.setUrl(url);
        return this;
    }

    public MultipartBodyRequestBuilder httpMethod(HttpMethod httpMethod) {
        request.setHttpMethod(httpMethod);
        return this;
    }

    public MultipartBodyRequestBuilder addParameter(String key, String value) {
        request.getParameters().put(key, value);
        return this;
    }

    public MultipartBodyRequestBuilder addParameters(Map<String, String> parameters) {
        if (parameters != null) {
            request.getParameters().putAll(parameters);
        }
        return this;
    }

    public MultipartBodyRequestBuilder addHeader(String key, String value) {
        request.getHeaders().put(key, value);
        return this;
    }

    public MultipartBodyRequestBuilder addHeaders(Map<String, String> headers) {
        if (headers != null) {
            request.getHeaders().putAll(headers);
        }
        return this;
    }

    public MultipartBodyRequestBuilder addFormData(String name, String value) {
        Part part = new Part();
        part.headers.put("Content-Disposition", "form-data; name=" + name);
        part.setContent(value.getBytes());

        this.parts.add(part);
        return this;
    }

    public MultipartBodyRequestBuilder addFile(String name, String fileName, byte[] file) {
        Part part = new Part();
        part.headers.put("Content-Disposition", "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\"");
        part.headers.put("Content-Type", "application/octet-stream");
        part.setContent(file);

        this.parts.add(part);
        return this;
    }

    public Request build() {

        byte[] content = partsToByteArray();
        request.setContent(content);
        request.setContentLength(content.length);

        return request;
    }

    private byte[] partsToByteArray() {

        byte[] bytes = {};

        String head = "--" + this.boundary + "\r\n";
        bytes = ArrayUtils.addAll(bytes, head.getBytes());


        for (int i = 0; i < this.parts.size(); i++) {
            Part part = parts.get(i);

            // header
            for (Map.Entry<String, String> entry : part.getHeaders().entrySet()) {

                String header = entry.getKey() + ": " + entry.getValue() + "\r\n";
                bytes = ArrayUtils.addAll(bytes, header.getBytes());
            }

            bytes = ArrayUtils.addAll(bytes, "\r\n".getBytes());

            bytes = ArrayUtils.addAll(bytes, part.getContent());


            bytes = ArrayUtils.addAll(bytes, "\r\n".getBytes());


            // 分割符
            bytes = ArrayUtils.addAll(bytes, ("--" + this.boundary).getBytes());
        }
        bytes = ArrayUtils.addAll(bytes, "--".getBytes());

        return bytes;
    }

    private static class Part {

        private Map<String, String> headers = new LinkedHashMap<>();

        /**
         * part content
         */
        private byte[] content;

        @Override
        public String toString() {
            return "Part{" +
                    "headers=" + headers +
                    ", content='" + content.length + '\'' +
                    '}';
        }

        public Map<String, String> getHeaders() {
            return headers;
        }

        public void setHeaders(Map<String, String> headers) {
            this.headers = headers;
        }

        public byte[] getContent() {
            return content;
        }

        public void setContent(byte[] content) {
            this.content = content;
        }
    }
}
